﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows;
using System.Windows.Data;

namespace ShredTheLog.Controls
{
    /// <summary>
    /// Таймер со свойствами поддерживающими binding
    /// </summary>
    /// <remarks>
    /// Обертка над DispatcherTimer, которая нужна из-за того, что DispatcherTimer не является потомком DependencyObject и 
    /// не может иметь dependency properties, которые нужны для поддержки binding.
    /// Данный класс пришлось пронаследовать от CollectionViewSource, а не от DependencyObject напрямую, 
    /// чтобы объекты данного класса получали inheritance context. Теоретически DependencyObject тоже может получать
    /// inheritance context, но все связанные с этим методы объявлены как internal и, соответственно, 
    /// не могут быть переопределены или использованы из пользовательского кода. Свойства, унаследованные от CollectionViewSource 
    /// следует игнорировать при работе с объектами данного класса.
    /// Решение кривое, но оно продиктовано кривостью базовой библиотеки. Возможно, в следующих версиях Framework появится нечто, 
    /// что позволит переписать этот участок более корректно.
    /// </remarks>
    public sealed class Timer : CollectionViewSource
    {
        private DispatcherTimer _internalTimer = new DispatcherTimer();

        public Timer()
        {
            _internalTimer.Tick += (sender, e) => Execute();
        }

        private void Execute()
        {
            if (Command != null && Command.CanExecute(null))
                Command.Execute(null);
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(Timer), new PropertyMetadata(null, CommandChangedCallback));
        private static void CommandChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = (Timer)d;
            if (sender.IsEnabled && sender.FireOnStart && e.NewValue != null && !Object.Equals(e.OldValue, e.NewValue))
                sender.Execute();
        }

        public TimeSpan Interval
        {
            get { return (TimeSpan)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }
        public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(TimeSpan), typeof(Timer), new PropertyMetadata(new TimeSpan(0), IntervalChangedCallback));
        private static void IntervalChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Timer)d)._internalTimer.Interval = (TimeSpan)e.NewValue;
        }

        public bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }
        // Using a DependencyProperty as the backing store for IsEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.Register("IsEnabled", typeof(bool), typeof(Timer), new PropertyMetadata(false, IsEnabledChangedCallback));
        private static void IsEnabledChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var timer = (Timer)d;
            timer._internalTimer.IsEnabled = (bool)e.NewValue;
            if (timer.FireOnStart && !(bool)e.OldValue && (bool)e.NewValue)
                timer.Execute();
        }

        /// <summary>
        /// Генерировать дополнительное однократное срабатывание таймера при запуске таймера
        /// </summary>
        public bool FireOnStart
        {
            get { return (bool)GetValue(FireOnStartProperty); }
            set { SetValue(FireOnStartProperty, value); }
        }
        public static readonly DependencyProperty FireOnStartProperty = DependencyProperty.Register("FireOnStart", typeof(bool), typeof(Timer), new PropertyMetadata(false, FireOnStartChangedCallback));
        private static void FireOnStartChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var timer = (Timer)d;
            if (timer.IsEnabled && !(bool)e.OldValue && (bool)e.NewValue)
                timer.Execute();
        }
    }

    public sealed class CommandExecutor : CollectionViewSource
    {
        private void Execute()
        {
            if (Command != null && Command.CanExecute(null))
                Command.Execute(null);
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandExecutor), new PropertyMetadata(null, CommandChangedCallback));
        private static void CommandChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = (CommandExecutor)d;
            if (e.NewValue != null && !Object.Equals(e.OldValue, e.NewValue))
                sender.Execute();
        }
    }
}
